﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Serialization;

namespace Genuilder.Extensibility
{
	public class DirectoryCodeRepository : CodeRepositoryBase
	{
		public class CodeItemMemento
		{
			//public CodeItemState State
			//{
			//    get;
			//    set;
			//}
			public string FileName
			{
				get;
				set;
			}
			public DateTime LastModified
			{
				get;
				set;
			}
		}

		public DirectoryCodeRepository(CodeItemMementoRepository repository)
		{
			if(repository == null)
				throw new ArgumentNullException("repository");
			_MementoRepository = repository;
			BaseDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());
		}
		public DirectoryInfo BaseDirectory
		{
			get;
			set;
		}
		private readonly CodeItemMementoRepository _MementoRepository;
		public CodeItemMementoRepository MementoRepository
		{
			get
			{
				return _MementoRepository;
			}
		}

		private readonly List<string> _Files = new List<string>();
		public List<string> Files
		{
			get
			{
				return _Files;
			}
		}
		protected override CodeItem CreateCodeItem(string name, CodeItemState state)
		{
			var item = new FileCodeItem(name, FullPathOf(name), state);
			base.ConfigureCodeItem(item);
			item.DependencyNode = Get(item);
			if(!item.File.Exists)
				item.OnContentChanged();
			item.File.Refresh();
			return item;
		}

		String FullPathOf(string name)
		{
			return Path.GetFullPath(Path.Combine(BaseDirectory.FullName, name));
		}

		protected override IEnumerable<CodeItemHistory> GetCodeItemHistories()
		{
			List<CodeItemHistory> histories = new List<CodeItemHistory>();
			var mementos = MementoRepository.LoadCodeItemMementos();

			foreach(var memento in mementos)
			{
				CodeItemHistory history = new CodeItemHistory(memento.FileName);
				var file = new FileInfo(FullPathOf(history.ItemName));
				if(!file.Exists)
					history.NewState = CodeItemState.Deleted;
				else
				{
					history.Modified = file.LastWriteTime > memento.LastModified;
					history.NewState = Files.Select(f => FullPathOf(f)).Contains(file.FullName) ? CodeItemState.Exist : CodeItemState.Excluded;
				}
				histories.Add(history);
			}


			var newFiles = Files.Where(f => !mementos.Any(m => m.FileName == f));
			foreach(var newFile in newFiles)
			{
				string codeItemName = PathUtil.RelativePathTo(BaseDirectory.FullName, newFile);
				var history = new CodeItemHistory(codeItemName);
				history.NewState = CodeItemState.Exist;
				history.Modified = true;
				histories.Add(history);
			}
			return histories;
		}



		private CodeItem CreateFromMemento(CodeItemMemento memento)
		{
			var item = CreateCodeItem(memento.FileName, CodeItemState.Exist);
			return item;
		}

		protected override void OnInitialized(IEnumerable<CodeItem> trackedItems)
		{
			var mementos = trackedItems.Where(i => i.State == CodeItemState.Exist).Select(i => ToMemento(i));
			MementoRepository.SaveCodeItemMementos(mementos);
		}



		private CodeItemMemento ToMemento(CodeItem item)
		{
			FileCodeItem itemFile = item as FileCodeItem;
			return new CodeItemMemento()
			{
				FileName = itemFile.Name,
				LastModified = itemFile.LastModified,

			};
		}

		//protected override CodeItemHistory GetHistory(CodeItem item)
		//{
		//    FileCodeItem fileCodeItem = (FileCodeItem)item;
		//    var mementos = MementoRepository.LoadCodeItemMementos();
		//    var itemMemento = mementos.FirstOrDefault(m => m.FileName == item.Name);
		//    CodeItemHistory history = new CodeItemHistory(item);

		//    if(itemMemento != null)
		//    {
		//        if(!fileCodeItem.File.Exists)
		//            history.NewState = CodeItemState.Deleted;
		//        else
		//        {
		//            history.Modified = fileCodeItem.LastModified > itemMemento.LastModified;
		//            history.NewState = Files.Select(f => new FileInfo(f).FullName).Contains(FullPathOf(fileCodeItem.Name)) ? CodeItemState.Exist : CodeItemState.Excluded;
		//        }
		//    }
		//    else
		//    {
		//        history.NewState = CodeItemState.Exist;
		//        history.Modified = true;
		//    }

		//    return history;
		//}

		FileCodeItem GetOrCreate(string codeItemName, Func<FileCodeItem> create)
		{
			var codeItem = GetFromRepository(codeItemName);
			if(codeItem != null)
				return (FileCodeItem)codeItem;

			codeItem = create();
			AddToRepository(codeItem);
			return (FileCodeItem)codeItem;
		}




		public override IEnumerable<string> RootSourceItemNames
		{
			get
			{
				return Files;
			}
		}

		public override IEnumerable<string> GeneratedSourceItemNames
		{
			get
			{
				var mementos = MementoRepository.LoadCodeItemMementos();
				return mementos.Select(m => m.FileName).Where(f => !Files.Contains(f));
			}
		}

		public override CodeItemHistory GetTransition(CodeItem item)
		{
			var histories = GetCodeItemHistories();
			return histories.FirstOrDefault(h => h.ItemName == item.Name);
		}



	}

	public class FileCodeItem : CodeItem
	{
		public FileCodeItem(string name, string fullPath, CodeItemState initialState)
			: base(name, initialState)
		{
			_FilePath = fullPath;
		}

		String _FilePath;
		public FileInfo File
		{
			get
			{
				return new FileInfo(_FilePath);
			}
		}

		protected override void OnDeleteCore()
		{
			File.Delete();
			File.Refresh();
		}

		string _Content;
		protected override string ContentCore
		{
			get
			{
				if(_Content == null)
				{
					_Content = LoadFileContent();
				}
				return _Content;
			}
			set
			{
				SaveFileContent(value);
				_Content = value;
				OnContentChanged();
			}
		}

		public DateTime LastModified
		{
			get
			{
				return File.LastWriteTime;
			}
		}


		private void SaveFileContent(string value)
		{
			if(!File.Exists && State == CodeItemState.Exist)
				File.Create().Close();
			System.IO.File.WriteAllText(File.FullName, value);
		}

		private string LoadFileContent()
		{
			if(!File.Exists)
				return "";
			return System.IO.File.ReadAllText(File.FullName);
		}
	}
}
